﻿using FyCaches.Core.Msg;
using FyCaches.Models;
using FySystem.Net.SimSocket;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FyCaches.Core.Services
{
    internal class MessageService
    {
        internal static List<SubscribeInfo> SubscribeInfos = new List<SubscribeInfo>();

        /// <summary>
        /// 订阅的垃圾回收
        /// </summary>
        /// <param name="client"></param>
        /// <exception cref="NotImplementedException"></exception>
        internal static void GC(SimClient client)
        {
            try
            {
                lock (SubscribeInfos)
                {
                    SubscribeInfo invalidSubscribe = null;

                    foreach(var item in SubscribeInfos)
                    {
                        SimClient invalidClient = null;
                        foreach(var conn in item.SimClients)
                        {
                            if (conn == client)
                            {
                                invalidClient = conn;
                                break;
                            }
                        }

                        if (invalidClient != null)
                            item.SimClients.Remove(invalidClient);
                        if (item.SimClients.Count <= 0)
                            invalidSubscribe = item;
                    }

                    if (invalidSubscribe != null)
                        SubscribeInfos.Remove(invalidSubscribe);
                }
            }
            catch (Exception ex)
            {
                FyLogContext.Write(ex.Message);
            }
        }

        /// <summary>
        /// 添加订阅信息
        /// </summary>
        /// <param name="topic"></param>
        /// <param name="commandInfo"></param>
        internal static void Add(string topic, CommandInfo commandInfo)
        {
            try
            {
                lock (SubscribeInfos)
                {
                    bool exist = false;
                    foreach(var item in SubscribeInfos)
                    {
                        if (item.Topic == topic)
                        {
                            item.SimClients.Add(commandInfo.SimClient);
                            exist = true;
                            break;
                        }
                    }

                    if (exist == false)
                    {
                        SubscribeInfos.Add(new SubscribeInfo()
                        {
                            Topic = topic,
                            Topics = topic.Split('/'),
                            SimClients=new List<SimClient>
                            {
                                commandInfo.SimClient
                            }
                        });
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 取消订阅
        /// </summary>
        /// <param name="topic"></param>
        /// <param name="command"></param>
        /// <exception cref="NotImplementedException"></exception>
        internal static void Delete(string topic, CommandInfo command)
        {
            try
            {
                lock (SubscribeInfos)
                {
                    SubscribeInfo empty = null;
                    foreach(var item in SubscribeInfos)
                    {
                        if(item.Topic == topic)
                        {
                            item.SimClients.Remove(command.SimClient);
                            if (item.SimClients.Count == 0)
                                empty = item;

                            break;
                        }
                    }

                    if (empty != null)
                        SubscribeInfos.Remove(empty);
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// 根据主题找出订阅的连接
        /// </summary>
        /// <param name="topic"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        internal static List<SubscribeInfo> GetTopicMatchConnections(string topic)
        {
            List<SubscribeInfo> res = new List<SubscribeInfo>();
            try
            {
                string[] topics = topic.Split('/');

                lock (SubscribeInfos)
                {
                    foreach (var item in SubscribeInfos)
                    {
                        if (IsTopicMatch(topics, item.Topics) || IsTopicMatch(item.Topics, topics))
                        {
                            res.Add(item);
                        }
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            return res;
        }

        /// <summary>
        /// 单向判断主题是否匹配，单层通配[+]，多层通配[*]
        /// 暂时没什么好的方式，只能进行两次匹配
        /// </summary>
        /// <param name="topics1"></param>
        /// <param name="topics2"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        internal static bool IsTopicMatch(string[] topics1, string[] topics2)
        {
            bool res = true;
            try
            {
                int topic2Index = 0;
                bool isPreCommon = false;

                for(int i = 0; i < topics1.Length; i++)
                {
                    //如果topic2已经全都匹配完了
                    if (topic2Index >= topics2.Length)
                    {
                        if (topics1[i] == "*")
                        {
                            bool flag = false;
                            if (i < topics1.Length - 1)
                            {
                                //判断后面还有没有非通配符的
                                for (int j = i + 1; j < topics1.Length; j++)
                                {
                                    if (topics1[j] != "*" && topics1[j] != "+")
                                    {
                                        flag = true;
                                        break;
                                    }
                                }

                                if (flag)
                                {
                                    res = false;
                                    break;
                                }
                            }
                            else
                            {
                                //如果后面都是通配符的，则认为是匹配
                                break;
                            }
                        }
                        else
                        {
                            res = false;
                            break;
                        }
                    }

                    if (isPreCommon == true)
                    {
                        //如果前一个是多层通配符，并且topic1中多层通配符后还会有子主题，简单起见，不支持类似：aa/bb/*/+/dd这样的写法，即多层通配符后不能紧跟单层通配符
                        for(int j = topic2Index; j < topics2.Length; j++)
                        {
                            if (topics1[i] == topics2[j])
                            {
                                topic2Index = j + 1;
                                isPreCommon = false;
                                break;
                            }
                        }

                        if (isPreCommon)
                        {
                            //说明其后一个能匹配上的都没有
                            res = false;
                            break;
                        }
                    }
                    else
                    {
                        if (topics1[i] != topics2[topic2Index])
                        {
                            //如果字符串不一样
                            if (topics1[i] == "*")
                            {
                                if (i == topics1.Length - 1)
                                {
                                    //如果多层通配符是最后一个，不管topic2是什么，都认为是匹配的
                                    break;
                                }
                                else
                                {
                                    //如果多层通配符不是最后一个
                                    isPreCommon = true;
                                    topic2Index++;
                                    continue;
                                }
                            }
                            else if (topics1[i] == "+")
                            {
                                topic2Index++;
                                continue;
                            }
                            else
                            {
                                res = false;
                                break;
                            }
                        }
                        else
                        {
                            //表示匹配到了
                            topic2Index++;
                        }
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            return res;
        }
    }
}
